package com.zone.mybatis.reflection;

import com.zone.mybatis.reflection.invoker.Invoker;
import com.zone.mybatis.reflection.property.PropertyTokenizer;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;

/**
 * @author: zongzi
 * @description: TODO
 * @date: 2024/1/27
 */
public class MetaObject {
    // 原始对象
    private final Object originObject;
    private final MetaClass metaClass;

    private MetaObject(Object originObject) {
        this.originObject = originObject;
        metaClass = MetaClass.forClass(originObject.getClass());
    }

    public static MetaObject forObject(Object o) {
        return new MetaObject(o);
    }

    // 班级[0].学生.id
    public Object get(String name) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        if (prop.hasNext()) {
            Object beanProperty = this.getByBeanProperty(prop.getIndexName());
            MetaObject metaObject = MetaObject.forObject(beanProperty);
            return metaObject.get(prop.getChildren());
        } else {
            return this.getByBeanProperty(name);
        }
    }

    private Object getByBeanProperty(String name) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        // index不为null = propertyName[]，存在数据为集合
        if (prop.getIndex() != null) {
            Object beanProperty = this.getByBeanProperty(prop.getName());
            if (beanProperty instanceof List) {
                int index = Integer.parseInt(prop.getIndex());
                return ((List) beanProperty).get(index);
            } else if (beanProperty instanceof Map) {
                return ((Map) beanProperty).get(prop.getIndex());
            }
        } else {
            Invoker invoker = metaClass.getGetInvoker(name);
            if (invoker == null) {
                throw new RuntimeException("Class: " + metaClass.getType().getName() + ", proper name: " + name + " not found!");
            }
            try {
                return invoker.invoke(originObject, null);
            } catch (InvocationTargetException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        throw new RuntimeException("get bean property only support List、Map and bean property name! name: " + name);
    }

    public void set(String name, Object value) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        if (prop.hasNext()) {
            Object beanProperty = this.getByBeanProperty(prop.getName());
            MetaObject metaObject = MetaObject.forObject(beanProperty);
            metaObject.set(prop.getChildren(), value);
        } else {
            this.setBeanProperty(name, value);
        }
    }

    private void setBeanProperty(String name, Object value) {
        Invoker setInvoker = metaClass.getSetInvoker(name);
        Object[] params = new Object[]{value};
        try {
            setInvoker.invoke(originObject, params);
        } catch (InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    public boolean hasSetter(String name) {
        return metaClass.hasSetter(name);
    }

    public Class<?> getSetType(String name) {
        return metaClass.getSetType(name);
    }
}
